----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    15:14:52 05/11/2012 
-- Design Name: 
-- Module Name:    multiplexer - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--	Using PCA9536(4-bit GPIO I2C-bus I/O expander) to set the AMUX_S(3 DOWNTO 0) value for ADG706(multiplexer).
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity multiplexer is
	generic (
		constant BIT_WIDTH : integer := 8		--DEFAULT: ONE BYTE
			);
    Port ( CLK : in  STD_LOGIC;
           SCL : out  STD_LOGIC;
           SDA : inout  STD_LOGIC;
           AMUX_S : in  STD_LOGIC_VECTOR (3 downto 0));
end multiplexer;

architecture Behavioral of multiplexer is
--------------------------SIGNAL---------------------------------
	type GPIO_I2C_STATE_TYPE is 
		(	
			st_conf_addr,
			st_conf_cmd,
			st_conf_data,
			st_conf_stop,
			
			st_output_ports_addr,
			st_output_ports_cmd,
			st_output_ports_data,
			st_output_ports_stop,
		
			st_start,			
			st_write,	
			st_wait_for_ack,
			st_stop	

		);

	signal state, state_next, state_pre      : GPIO_I2C_STATE_TYPE := st_conf_addr;
	
	SIGNAL step_counter					: unsigned(1 downto 0) 	:= "00";
	------------------------------------------------------------------
begin
	
	
	PROCESS (CLK) 
	
		-------------------------------------------------------------------------------
		variable data_to_write			: std_logic_vector(BIT_WIDTH-1 downto 0) := x"00";
		variable is_updated				: std_logic := '0';
		
		CONSTANT SLAVE_ADDR				: std_logic_vector(6 downto 0) := "1000001";		
		CONSTANT OUTPUT_PORT_REGISTER			: std_logic_vector(7 downto 0) := x"01";
		CONSTANT CONF_REGISTER			: std_logic_vector(7 downto 0) := x"03";
		
		variable amux_s_internal		: std_logic_vector(3 downto 0);
		
--		variable step_counter					: unsigned(1 downto 0) 	:= "00";
		variable bit_counter						: integer range 0 to BIT_WIDTH  := 0;
		--number of cycles will wait for the 'ACK' from slave
		variable wait_counter					: integer range 0 to 255 := 0;
		-------------------------------------------------------------------------------
	BEGIN		
		IF RISING_EDGE(CLK) THEN
			
			if is_updated = '0' then	--if not updated yet, jump to state machine and send the AMUX_S value.
						---------------------START CASE STATE--------------------------------
					CASE state IS
	
					-------------------------------------------------------------
					WHEN st_conf_addr =>
						data_to_write(7 downto 0) := SLAVE_ADDR & '0';	--'0' to write
						state <= st_start;
						state_next	<= st_conf_cmd;
						state_pre 	<= st_conf_addr;
						
					WHEN st_conf_cmd =>
						data_to_write(7 downto 0) := CONF_REGISTER;
						state <= st_start;
						state_next	<= st_conf_data;
						state_pre 	<= st_conf_cmd;
						
					WHEN st_conf_data =>
						data_to_write(7 downto 0) := x"00";		--'0':output;		'1'(default):input.
						state <= st_start;
						state_next	<= st_conf_stop;
						state_pre 	<= st_conf_data;
					
					WHEN st_conf_stop =>
						state			<= st_stop;
						state_next	<= st_output_ports_addr;
						state_pre 	<= st_conf_data;
					-------------------------------------------------------------
					
					WHEN st_output_ports_addr => 
						data_to_write(7 downto 0) := SLAVE_ADDR & '0';	--'0' to write
						state <= st_start;
						state_next	<= st_output_ports_cmd;
						state_pre 	<= st_output_ports_addr;
					WHEN st_output_ports_cmd => 
						data_to_write(7 downto 0) := OUTPUT_PORT_REGISTER;
						state <= st_start;
						state_next	<= st_output_ports_data;
						state_pre 	<= st_output_ports_cmd;
					WHEN st_output_ports_data => 
						data_to_write(7 downto 0) := x"0" & amux_s_internal;	--output the AMUX_S(3 DOWNTO 0) value to the multiplexer.
						state <= st_start;
						state_next	<= st_output_ports_stop;
						state_pre 	<= st_output_ports_data;
					WHEN st_output_ports_stop =>
						state			<= st_stop;
						state_next	<= st_conf_addr;
						state_pre 	<= st_output_ports_stop;
					-------------------------------------------------------------
					-------------------------------------------------------------	
					WHEN st_start =>
						if (step_counter = 0) then		
							SCL <= '0';
							step_counter <= step_counter + 1;
						elsif (step_counter = 1) then
							SDA <= 'Z';
--							SDA <='1';
							step_counter <= step_counter + 1;
						elsif (step_counter = 2) then
							SCL <= 'Z';
							step_counter <= step_counter + 1;
						else
							SDA <= '0';
							step_counter <= "00";
							state <= st_write;
						end if;	
					-------------------------------------------------------------			
					WHEN st_write =>
						if bit_counter < BIT_WIDTH then   
							if step_counter = 0 then 
								SCL <= '0';
								step_counter <= step_counter + 1;
							elsif step_counter = 1 then
--								SDA <= data_to_write(BIT_WIDTH-1 - bit_counter);
								if data_to_write(BIT_WIDTH-1 - bit_counter) = '1' then
									SDA <= 'Z';
								else
									SDA <= '0';
								end if;
								step_counter <= step_counter + 1;
							elsif step_counter = 2 then
								SCL <= 'Z';
								bit_counter := bit_counter + 1;
								step_counter <= "00";
							end if;
						else   --after 1 byte is sent
							bit_counter := 0;
							state <= st_wait_for_ack;
								
						end if;
								

					-------------------------------------------------------------		
					WHEN st_wait_for_ack =>
						
						if step_counter = 0 then
							SCL <= '0';
							step_counter <= step_counter + 1;
						elsif step_counter = 1 then
							SDA <= 'Z';
							step_counter <= step_counter + 1;
						elsif step_counter = 2 then
							SCL <= 'Z';
							step_counter <= step_counter + 1;
						elsif step_counter = 3 then
							if SDA = '0' then								
									step_counter <= "00";
									state <= state_next;						
							else
								wait_counter := wait_counter + 1;
								if wait_counter > 200 then		--wait 200 cycles if not receiving 'ack' from slave				
										step_counter <= "00";
										wait_counter := 0;
										state <= state_pre;								
								end if;
							end if;
						end if;
								
					-------------------------------------------------------------			
					
					-------------------------------------------------------------		
					WHEN st_stop =>
						
						if step_counter = 0 then
							SCL <= '0';
							step_counter <= step_counter + 1;
						elsif step_counter = 1 then
							SDA <= '0';
							step_counter <= step_counter + 1;
						elsif step_counter = 2 then
							SCL <= 'Z';
							step_counter <= step_counter + 1;
						else
--							SDA <= '1';
							SDA <= 'Z';
							step_counter <= "00";
							
							if state_pre = st_output_ports_stop then
								is_updated := '1';
							end if;
							
							state <= state_next;
						end if;
						
						
						
					-------------------------------------------------------------		
					WHEN others =>
						step_counter <= "00";
						wait_counter := 0;
						state <= state_pre;
					
					END CASE;		
					---------------------END CASE STATE--------------------------------
			else	--already updated
				if	amux_s_internal /= AMUX_S then	--if there are new AMUX_S value to be set.
					amux_s_internal := AMUX_S;
					is_updated := '0';
				end if;
			end if;
			
		END IF;  --RISING_EDGE(CLK)
	END PROCESS;

end Behavioral;